use std::{
    fs,
    path::{Path, PathBuf},
};

use serde::{Deserialize, Serialize};

use crate::{m3u8_key::Key, m3u8_parser::get_ts_list_and_key};

#[derive(Serialize, Deserialize, Debug)]
pub struct HistoryArgs {
    pub m3u8_url: String,
    pub name: String,
    pub dir_path_str: String,
    pub batch_size: usize,
    pub key: Option<Key>,
    pub ts_url_list: Option<Vec<String>>,
}

impl HistoryArgs {
    pub async fn from_cli() -> Self {
        const BATCH_SIZE: usize = 50;

        let name = std::env::args().nth(1).expect("输入名称");

        let dir_path_str = format!("./{name}_tmp");

        let ts_dir = Path::new(&dir_path_str);

        if !ts_dir.exists() {
            fs::create_dir_all(ts_dir).unwrap();
        }

        // 从第{n}参数获取batch_size参数
        let get_batch_size_fn = |n| -> Option<usize> {
            std::env::args().nth(n).and_then(|it| {
                let Ok(size) = it.parse::<usize>() else { return None };
                Some(size)
            })
        };

        // 是否存在历史数据 如果存在 则自本地加载信息
        let local_file_path = ts_dir.join(".history");
        if local_file_path.exists() {
            // 若有历史数据第二个或第三个参数需是batch_size
            // 又或者没有 则指定默认值
            let batch_size = get_batch_size_fn(2)
                .or(get_batch_size_fn(3))
                .unwrap_or_else(|| BATCH_SIZE);

            return Self::from_history(local_file_path.clone(), batch_size);
        }

        // 不存在本地数据 则第二参数必须是URL
        let m3u8_url = std::env::args().nth(2).expect("输入m3u8地址");

        // 根据URL获取其他信息
        let (ts_url_list, key) = match get_ts_list_and_key(&m3u8_url).await {
            Ok(_res) => _res,
            Err(err) => panic!("{}", err),
        };

        if ts_url_list.is_empty() {
            panic!("TS 列表为空")
        }

        // 保存从URL获取的信息

        // batch_size的参数是可选的,如果不存在则指定默认值
        let batch_size = get_batch_size_fn(3).unwrap_or_else(|| BATCH_SIZE);
        let dir_path_str = ts_dir
            .canonicalize()
            .unwrap()
            .into_os_string()
            .into_string()
            .unwrap();

        let mut args = HistoryArgs {
            m3u8_url,
            name,
            dir_path_str,
            batch_size,
            key: None,
            ts_url_list: Some(ts_url_list),
        };

        args.set_key(key);

        let json_data = serde_json::to_string(&args).unwrap();
        fs::write(local_file_path, json_data).unwrap();

        args
    }

    /// 从历史数据中获取
    pub fn from_history(local_file_path: PathBuf, batch_size: usize) -> Self {
        // TODO 异常处理
        let data = fs::read_to_string(local_file_path).unwrap();
        // TODO 异常处理
        let mut args = serde_json::from_str::<HistoryArgs>(&data).unwrap();
        args.batch_size = batch_size;
        args
    }

    /// 设置Key
    pub fn set_key(&mut self, key: Key) -> &Self {
        self.key = Some(key);
        self
    }

    /// 设置Ts列表信息
    pub fn set_ts_url_list(&mut self, ts_url_list: Vec<String>) -> &Self {
        self.ts_url_list = Some(ts_url_list.clone());
        self
    }
}

#[cfg(test)]
mod _test {
    use crate::{
        m3u8_env::HistoryArgs,
        m3u8_key::{Key, KeyType},
    };

    #[test]
    fn test_serialize() {
        let key = Key::default();

        let args = HistoryArgs {
            m3u8_url: "".to_string(),
            name: "test".to_string(),
            dir_path_str: "./test".to_string(),
            batch_size: 50,
            key: Some(key),
            ts_url_list: None,
        };

        let json = serde_json::to_string(&args);

        println!("json={json:#?}");
    }

    #[test]
    fn test_serialize2() {
        let _key = Key {
            ty: KeyType::Aes128,
            iv: Some("".to_string()),
            content: b"kkkkkkkkkkkk".to_vec(),
        };

        let mut args = HistoryArgs {
            m3u8_url: "".to_string(),
            name: "test".to_string(),
            dir_path_str: "./test".to_string(),
            batch_size: 50,
            key: None,
            ts_url_list: None,
        };

        args.set_key(_key);

        let json = serde_json::to_string(&args);

        println!("json={json:#?}");
    }
}
